package fr.uvsq.isty.bdh.hchess.gui.game;

import fr.uvsq.isty.bdh.hchess.engine.GameData;
import fr.uvsq.isty.bdh.hchess.gui.style.Style;
import ictk.boardgame.chess.ChessBoard;
import ictk.boardgame.chess.ChessPiece;
import ictk.boardgame.chess.Square;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JPanel;

/**
 * A Jpanel that allow to display the board with the pieces on it.
 * @version 1.0
 * @author Florent Hemmi <florent.hemmi@gmail.com>
 * @author Rémi Dubois <RemiEric.Dubois@gmail.com>
 */
public class BoardDisplay extends JPanel implements Observer {
    
    public static final int DEFAULT_WIDTH = 500;  /** Draw panel width */
    public static final int DEFAULT_HEIGHT = 500; /** Draw panel height */
    public static final int DEFAULT_CASE_WIDTH = (int)(DEFAULT_WIDTH/ictk.boardgame.chess.ChessBoard.MAX_FILE);
    public static final int DEFAULT_CASE_HEIGHT = (int)(DEFAULT_HEIGHT/ictk.boardgame.chess.ChessBoard.MAX_RANK);
     /** Padding between the internal margin and the image */
    public static final int CASE_PADDING = 5;
    
    private ChessBoard board; /** ChessBoard (provide by ICTK) */
    
    private Style style; /** Board style singleton */
    
    private ChessPiece pickedPiece;
    private List<Square> pickedPiecePossibleMoves;
    private int pickedNewPosX;
    private int pickedNewPosY;
    private boolean lastMoveExist;
    private int lastMoveOriginX;
    private int lastMoveOriginY;
    private int lastMoveDestinationX;
    private int lastMoveDestinationY;
    
    private boolean gameOver;
    private boolean promoting;
    
    private ChessPiece promotedPiece;
    private Square promotedDestination;
    
    private boolean helpEnable;
    
    
    public BoardDisplay() {
        this.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
        
        try {
            this.style = Style.getInstance();
        } catch (IOException ex) {
        }
        
        board = null;
        
        pickedPiece = null;
        pickedPiecePossibleMoves = null;
        lastMoveExist = false;
        
        gameOver = false;
        
        helpEnable = true;
    }
    
    @Override
    public void paintComponent(Graphics g) {

        g.setColor(Color.WHITE);
        g.fillRect(0, 0, this.getWidth(), this.getHeight());
        g.drawImage(style.getBoardImage(), 0, 0, this.getWidth(), this.getHeight(), this);


        if(board != null) {
            for (int i=1; i<=ictk.boardgame.chess.ChessBoard.MAX_RANK; i++) {
                for (int j=1; j<=ictk.boardgame.chess.ChessBoard.MAX_FILE; j++) {

                    Square currentSquare = board.getSquare(i,j);
                    ChessPiece piece = currentSquare.getOccupant();

                    if (lastMoveExist) {
                        if (currentSquare.equals(board.getSquare(lastMoveOriginX, lastMoveOriginY))) {
                            Image image = style.getMoveImage("lastMoveOrigin");
                            drawCase(g, i, j, image);
                        }
                        if (currentSquare.equals(board.getSquare(lastMoveDestinationX, lastMoveDestinationY))) {
                            Image image = style.getMoveImage("lastMoveDestination");
                            drawCase(g, i, j, image);
                        }
                    }

                    if (pickedPiece != null &&
                            pickedPiecePossibleMoves.contains(currentSquare) &&
                            helpEnable &&
                            !gameOver) {
                        if (piece != null || 
                                (pickedPiece.isPawn() &&
                                pickedPiece.isLegalAttack(currentSquare))) {
                            Image image = style.getMoveImage("possibleCapture");
                            drawCase(g, i, j, image);
                        }
                        else {
                            Image image = style.getMoveImage("possibleDestination");
                            drawCase(g, i, j, image);
                        }
                    }
                    // If there is a piece...
                    if (piece != null) {
                        if ((((pickedPiece != null &&
                                currentSquare.equals(pickedPiece.getSquare())))
                                || (promoting && 
                                currentSquare.equals(promotedPiece.getSquare())))
                                && !gameOver) {
                            Image image = style.getMoveImage("origin");
                            drawCase(g, i, j, image);
                        }
                        else if (promoting && currentSquare.equals(promotedDestination)) {
                            Image image = style.getPieceImage(promotedPiece.toString(), promotedPiece.isBlack());
                            drawCase(g, i, j, image);
                        }
                        else {
                            Image image = style.getPieceImage(piece.toString(), piece.isBlack());
                            drawCase(g, i, j, image);
                        }
                    }
                }
            }

            if (pickedPiece != null && !gameOver) {
                int pictureSizeX = this.getCaseWidth() - 2*CASE_PADDING;
                int pictureSizeY = this.getCaseHeight() - 2*CASE_PADDING;
                Image image = style.getPieceImage(pickedPiece.toString(), pickedPiece.isBlack());
                g.drawImage(image, pickedNewPosX-pictureSizeX/2, pickedNewPosY-pictureSizeY/2, pictureSizeX, pictureSizeY, this);
            }
        }
    }
    
    /**
     * Draw the image given in parameter in the case indicate by i and j.
     * 
     * @param i i coordinate (1-8)
     * @param j j coordinate (1-8)
     * @param img Image to display
     */
    private void drawCase(Graphics g, int i, int j, Image img) {
        if (i < 1 || i > 8 || j < 1 || j > 8) {
            throw new IndexOutOfBoundsException(
                    "Does not correspond to a case (1-8 required).");
        }
        
        int width = this.getWidth();
        int height = this.getHeight();
        float caseSizeX = ((float)width)/ictk.boardgame.chess.ChessBoard.MAX_RANK;
        float caseSizeY = ((float)height)/ictk.boardgame.chess.ChessBoard.MAX_FILE;
        int x = (int)((i-1)*caseSizeX) + CASE_PADDING;
        int y = (int)((ictk.boardgame.chess.ChessBoard.MAX_FILE-j)*caseSizeY) + CASE_PADDING;
        int pictureSizeX = (int)caseSizeX - 2*CASE_PADDING;
        int pictureSizeY = (int)caseSizeY - 2*CASE_PADDING;
        
        g.drawImage(img, x, y, pictureSizeX, pictureSizeY, this);
    }
    
    /**
     * @return the width of a case
     */
    public int getCaseWidth() {
        return this.getWidth()/ictk.boardgame.chess.ChessBoard.MAX_FILE;
    }

    /**
     * @return the height of a case
     */
    public int getCaseHeight() {
        return this.getHeight()/ictk.boardgame.chess.ChessBoard.MAX_RANK;
    }
     
    /**
     * get the game data unseful to the display
     * @param o the GameData
     * @param arg unused
     */
    public void update(Observable o, Object arg) {
        if(o instanceof GameData) {
            GameData gameData = (GameData) o;
            
            board = gameData.getBoard();
            pickedPiece = gameData.getPickedPiece();
            if(pickedPiece != null) {
                pickedPiecePossibleMoves = pickedPiece.getLegalDests();
            }
            pickedNewPosX = gameData.getPickedNewPosX();
            pickedNewPosY = gameData.getPickedNewPosY();
            
            int [] lastMoveCoordinates = gameData.getLastMoveCoordinates();
            if(lastMoveCoordinates != null) {
                lastMoveExist = true;
                lastMoveOriginX = lastMoveCoordinates[0];
                lastMoveOriginY = lastMoveCoordinates[1];
                lastMoveDestinationX = lastMoveCoordinates[2];
                lastMoveDestinationY = lastMoveCoordinates[3];
            }
            else {
                lastMoveExist = false;
            }
            
            gameOver = gameData.isGameOver();
            
            promoting = gameData.isPromoting();
            promotedPiece = gameData.getPromotedPiece();
            promotedDestination = gameData.getPromotedDestination();
            
            helpEnable = gameData.isHelpEnable();
            
            this.repaint();
        }
    }
}
